home *** CD-ROM | disk | FTP | other *** search
Text File | 2000-09-28 | 16.1 KB | 505 lines | [TEXT/CWIE] |
- unit DiskImageCore;
-
- (*
- File: DiskImageCore.p
-
- Contains: Core code for the disk image device driver.
-
- Written by: Quinn "The Eskimo!"
-
- Copyright: © 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample Code"
- after having made changes. If you're going to re-distribute the source,
- we require that you make it clear in the source that the code was
- descended from Apple Sample Code, but that you've made changes.
- *)
-
- interface
-
- uses
- Types,
- AppleTalk,
- Files,
- Devices;
-
- {$PUSH}
- {$ALIGN MAC68K}
-
- (* ***** DiskImageRecord ***** *)
-
- (* DiskImageRecord is the core data structure of the driver. It's a record
- extension of a drive queue element, with the drive queue element data
- structures at the front and the DiskImageCore data structures after that.
- So fields are controlled by this module, and some by the AysncDriverSample
- module. This goes against modularity a bit, but it makes life a lot easier.
- *)
-
- type
- DiskImageRecord =
- record
- (* The following fields make up a drive queue element and it's corresponding
- flags. See "IM: Files", p2-84 for a description of each of the components.
- *)
-
- (* The following 4 bytes must be in order and immediately in front of driveQElement
- because the system expects them to be there.
- *)
- driveFlags: SignedByte;
- diskInPlace: SignedByte;
- driveInstalled: SignedByte;
- numberOfSides: SignedByte;
- driveQElement: DrvQEl;
-
- (* The remaining fields are an extension of the drive queue element that
- holds all of our per-drive variables.
- *)
-
- diskSize: longint; (* The size of the disk in bytes. *)
-
- aspSessionNumber : integer; (* The ASP session number to the server holding the disk. *)
- afpForkRef : integer; (* The AFP fork refnum to the image file. *)
-
- (* driverDCE must immediately preceed xppParam because the GenericCompletion
- and the PascalCompletion routines in AsyncDriverSample relies on it.
- *)
- driverDCE : DCtlPtr;
-
- (* Allocate one XPPParamBlock and AFP command and result blocks per drive,
- which is excessive but safe. In general Mac DRVRs are single threaded,
- ie can only be handling one request at a time, so you would only need
- one copy of these things.
- Unfortunately we can't allocate these globally because the
- disk mounting call is done using an immediate call, which is not
- mutually excluded vis-a-vis outstanding asynchronous calls. So we
- could hit this situation:
-
- 1. System starts async request to Drive A.
- 2. Before that request completes, someone asks to mount a new disk
- image using an immediate call, which scrunges the globally
- allocated XPPParamBlock.
-
- There are ways I could get around this, such as allocating a temporary
- XPPParamBlock during the disk mount operation, but this was the cleanest
- (and easiest :) solution.
- *)
-
- xppParam : XPPParamBlock;
- commandBlock : packed array [0..1023] of Byte;
- resultBlock : packed array [0..1023] of Byte;
- end;
- DiskImageRecordPtr = ^DiskImageRecord;
-
- {$ALIGN RESET}
- {$POP}
-
- (* ***** External Routines ***** *)
-
- function InitDiskImageCore : OSErr;
- (* Initialise this module. *)
-
- function CreateDiskImage(diskImage : FSSpec; var disk : DiskImageRecordPtr) : OSErr;
- (* Creates a DiskImageRecord for accessing the specification diskImage file.
- This call must be made at SystemTask time.
- *)
-
- function ReadDiskImage(disk : DiskImageRecordPtr;
- offset : longint; count : longint; buffer : Ptr;
- ioCompletion : ProcPtr) : OSErr;
- (* Reads count bytes from a disk, at the given offset into the image,
- and puts it in the buffer. If ioCompletion is nil, the
- routine operates synchronously. If ioCompletion is not nil,
- the routine operates asynchronously and calls ioCompletion
- when it's done.
- This call may be made at interrupt time, as long as it's made
- asynchronously.
- *)
-
- function DestroyDiskImage(disk : DiskImageRecordPtr) : OSErr;
- (* Disposes a DiskImageRecord created by CreateDiskImage.
- This call must be made at SystemTask time.
- *)
-
- implementation
-
- uses
- OSUtils,
- AppleTalk,
- Devices,
- Memory,
- Errors,
- FSM;
-
- (* ***** Disk Copy Files ***** *)
-
- (* DiskCopyHeader defines the header for a DiskCopy disk image, ie file type 'dImg'. *)
-
- {$PUSH}
- {$ALIGN MAC68K}
-
- type
- DiskCopyHeader =
- record
- name: Str63;
- dataSize: longint;
- tagSize: longint;
- dataChecksum: longint;
- tagChecksum: longint;
- unknown: longint;
- end;
-
- {$ALIGN RESET}
- {$POP}
-
- (* ***** Borrowing an AFP Session Number ***** *)
-
- (* The following code was stolen in toto from "Technote NW 16 -- Borrowed AFP
- Sessions" <http://devworld.apple.com/dev/technotes/nw/nw_16.html>.
- You should read that for an explanation of what it does.
- *)
-
- {$PUSH}
- {$ALIGN MAC68K}
-
- CONST
- { AFP version numbers }
- AFPVer1_1 = 1; { AFP version 1.1 }
- AFPVer2_0 = 2; { AFP version 2.0 }
- AFPVer2_1 = 3; { AFP version 2.1 }
-
- AFPSVolInfo = 124; { server volume information call }
-
- TYPE
- GetVolSessInfoRec = RECORD
- sessAFPVersion: Integer; {AFP version number}
- sessReferenceNumber: Integer; {session reference number}
- sessAFPVolID: Integer; {AFP volume identifier}
- sessServerAddress: AddrBlock; {server internet address}
- sessUAMType: Integer; {user authentication method}
- sessUserNamePtr: StringPtr; {ptr to user name string}
- sessVolIconPtr: Ptr; {ptr to server volume icon/mask}
- sessWhereStringPtr: StringPtr; {ptr to "where" information string}
- END;
-
- {$ALIGN RESET}
- {$POP}
-
- FUNCTION GetVolSessionInfo (theVRefNum: Integer;
- VAR theVolSessInfoRec: GetVolSessInfoRec): OSErr;
- CONST
- TSigWord = $4244; { HFS volume signature }
- VAR
- pb: ParamBlockRec;
- aVCB: VCBPtr;
- afpTranslatorRefNum: Integer;
- err: OSErr;
- BEGIN
- { get the .AFPTranslator driver refNum }
- err := OpenDriver('.AFPTranslator', afpTranslatorRefNum);
- IF err <> noErr THEN
- BEGIN { couldn't open the driver }
- GetVolSessionInfo := err;
- Exit(GetVolSessionInfo);
- END;
-
- { find the VCB with the volume reference number }
- aVCB := VCBPtr(GetVCBQHdr^.qHead); { pointer to first VCB }
- WHILE (aVCB <> NIL) DO
- BEGIN
- IF aVCB^.vcbSigWord = TSigWord THEN { must be HFS volume }
- IF aVCB^.vcbVRefNum = theVRefNum THEN
- Leave; { we found the VCB }
- aVCB := VCBPtr(aVCB^.qLink); { next VCB }
- END;
- IF (aVCB = NIL) THEN
- BEGIN { couldn't find the volume }
- GetVolSessionInfo := nsvErr;
- Exit(GetVolSessionInfo);
- END;
-
- { make the status call to get the volume session info }
- WITH pb DO
- BEGIN
- ioRefNum := afpTranslatorRefNum;
- csCode := AFPSVolInfo;
- ioMisc := Ptr(aVCB);
- ioBuffer := @theVolSessInfoRec;
- ioReqCount := LongInt(sizeof(GetVolSessInfoRec));
- END;
- GetVolSessionInfo := PBStatusSync(@pb);
- END;
-
- (* ***** Globals ****** *)
-
- var
- gXPPRefNum : integer; (* Driver refNum of the XPP driver. *)
-
- (* ***** AFP Command Build and Execute ****** *)
-
- (* The following routines are simple utilites to facility the building
- and execution of AFP commands. The XPP driver allows you to send
- AFP commands, but that's about it. It doesn't provide a decent
- high-level API. So, when you build commands, you have to ensure
- that they map byte-for-byte to the command on the wire. The
- easiest way to do this is to build the commands one byte at a time,
- rather than try to match your structures to structure of the command.
- *)
-
- procedure StartAFPCommand(disk : DiskImageRecordPtr; cmdByte : integer);
- (* Called when starting a new AFP command. cmdByte is the command
- we're starting.
- *)
- begin
- disk^.commandBlock[0] := cmdByte;
- disk^.xppParam.cbSize := 1;
- disk^.xppParam.cbPtr := @disk^.commandBlock;
- end; (* StartAFPCommand *)
-
- procedure AddAFPByte(disk : DiskImageRecordPtr; byte : integer);
- (* Add a single byte to the current AFP command. *)
- begin
- disk^.commandBlock[disk^.xppParam.cbSize] := byte;
- disk^.xppParam.cbSize := disk^.xppParam.cbSize + 1;
- end; (* AddAFPByte *)
-
- procedure AddAFPWord(disk : DiskImageRecordPtr; word : integer);
- (* Add a two byte word to a command, high byte first. *)
- begin
- disk^.commandBlock[disk^.xppParam.cbSize] := band(bsr(word, 8), $0FF);
- disk^.commandBlock[disk^.xppParam.cbSize + 1] := band(word, $0FF);
- disk^.xppParam.cbSize := disk^.xppParam.cbSize + 2;
- end; (* AddAFPWord *)
-
- procedure AddAFPLong(disk : DiskImageRecordPtr; long : longint);
- (* Add a four byte long to a command, high byte first. *)
- begin
- disk^.commandBlock[disk^.xppParam.cbSize] := band(bsr(long, 24), $0FF);
- disk^.commandBlock[disk^.xppParam.cbSize + 1] := band(bsr(long, 16), $0FF);
- disk^.commandBlock[disk^.xppParam.cbSize + 2] := band(bsr(long, 8), $0FF);
- disk^.commandBlock[disk^.xppParam.cbSize + 3] := band(long, $0FF);
- disk^.xppParam.cbSize := disk^.xppParam.cbSize + 4;
- end; (* AddAFPLong *)
-
- procedure AddAFPString(disk : DiskImageRecordPtr; str : Str255);
- (* Add a Pascal string to a command. In AFP, Pascal strings are
- packed in their minimal size.
- *)
- begin
- BlockMove(@str[0], @disk^.commandBlock[disk^.xppParam.cbSize], length(str) + 1);
- disk^.xppParam.cbSize := disk^.xppParam.cbSize + length(str) + 1;
- end; (* AddAFPString *)
-
- function RunAFPCommand(disk : DiskImageRecordPtr; ioCompletion : ProcPtr) : OSErr;
- (* Execute the current AFP command synchronously or asynchronously. *)
- const
- kStandardASPTimeout = 5;
- var
- err : OSStatus;
- begin
- (* Fill out the param block. *)
- disk^.xppParam.ioCompletion := ioCompletion;
- disk^.xppParam.ioRefNum := gXPPRefNum;
- disk^.xppParam.csCode := afpCall;
- disk^.xppParam.sessRefnum := disk^.aspSessionNumber;
- disk^.xppParam.aspTimeout := kStandardASPTimeout;
-
- (* Run the command. *)
- err := AFPCommand(@disk^.xppParam, ioCompletion <> nil);
-
- (* If we're running synchronously, ignore the error from the command
- and return ioInProgress (ie 1).
- *)
- if ioCompletion <> nil then begin
- err := ioInProgress;
- end; (* if *)
-
- (* If we don't have an error, get the error out of the XPP paramblock.
- Note that, because of the above check, this only happens if
- we're running synchronously.
- *)
- if err = noErr then begin
- err := disk^.xppParam.cmdResult;
- end; (* if *)
-
- RunAFPCommand := err;
- end; (* RunAFPCommand *)
-
- (* ***** Initialisation ***** *)
-
- function InitDiskImageCore : OSErr;
- (* See comment in implementation part. *)
- var
- err : OSErr;
- begin
- err := OpenDriver('.XPP', gXPPRefNum);
- InitDiskImageCore := err;
- end; (* InitDiskImageCore *)
-
- (* ***** AFP Open/Close ****** *)
-
- (* Routines for issuing AFP commands to open and/or close a fork. *)
-
- function OpenAFPFork(disk : DiskImageRecordPtr; volumeID : integer;
- parID : longint;
- name : Str63) : OSErr;
- (* Opens the data fork of the file an AFP server. Note that parID
- is derived from a local FSSpec, making the implicit
- assumption the AFP "Directory ID" field maps 1-1 with
- the parID in the FSSpec returned by the local AFP client
- software. Not doing this would be bizarre, but possible.
- This call operates synchronously.
- *)
- var
- err : OSStatus;
- begin
- (* Build the AFP command. *)
- StartAFPCommand(disk, afpOpenFork);
- AddAFPByte(disk, 0); (* "Rsrc/Data Flag" = data fork *)
- AddAFPWord(disk, volumeID); (* "Volume ID" *)
- AddAFPLong(disk, parID); (* "Directory ID" *)
- AddAFPWord(disk, 0); (* "Bitmap" = request no extra info *)
- AddAFPWord(disk, 1); (* "Access Mode" = read access *)
- AddAFPByte(disk, 2); (* "Path Type" = long names *)
- AddAFPString(disk, name); (* "Pathname" *)
-
- (* Run the command. *)
- disk^.xppParam.rbSize := sizeof(disk^.resultBlock);
- disk^.xppParam.rbPtr := @disk^.resultBlock;
- disk^.xppParam.wdSize := 0; (* Not used by afpOpenFork *)
- disk^.xppParam.wdPtr := nil; (* ditto *)
- err := RunAFPCommand(disk, nil);
-
- (* Extract the fork refnum from the two bytes at offset 2 of the result block. *)
- if err = noErr then begin
- disk^.afpForkRef := bsl(disk^.resultBlock[2], 8) + disk^.resultBlock[3];
- end; (* if *)
-
- OpenAFPFork := err;
- end; (* OpenAFPFork *)
-
- function ReadAFPData(disk : DiskImageRecordPtr;
- offset : longint; count : longint; buffer : Ptr;
- ioCompletion : ProcPtr) : OSErr;
- (* See comment in implementation part. *)
- var
- err : OSStatus;
- begin
- (* Build the AFP command. *)
- StartAFPCommand(disk, afpRead);
- AddAFPByte(disk, 0); (* pad *)
- AddAFPWord(disk, disk^.afpForkRef); (* "OForkRefNum" *)
- AddAFPLong(disk, offset); (* "Offset" *)
- AddAFPLong(disk, count); (* "ReqCount" *)
- AddAFPByte(disk, 0); (* "Newline Mask" *)
- AddAFPByte(disk, 0); (* "Newline Char" *)
-
- (* Run the command. Note that XPP special cases afpRead calls,
- handling multiple packet transactions transparently to us.
- *)
- disk^.xppParam.rbPtr := buffer;
- err := RunAFPCommand(disk, ioCompletion);
-
- ReadAFPData := err;
- end; (* ReadAFPData *)
-
- function CloseAFPFork(disk : DiskImageRecordPtr) : OSErr;
- var
- err : OSStatus;
- (* Closes the AFP fork synchronously. *)
- begin
- (* Build the AFP command. *)
- StartAFPCommand(disk, afpForkClose);
- AddAFPByte(disk, 0); (* pad *)
- AddAFPWord(disk, disk^.afpForkRef); (* "OForkRefNum" *)
-
- (* Run the command. *)
- disk^.xppParam.rbSize := 0;
- disk^.xppParam.rbPtr := nil;
- disk^.xppParam.wdSize := 0; (* Not used by afpForkClose *)
- disk^.xppParam.wdPtr := nil; (* ditto *)
- err := RunAFPCommand(disk, nil);
-
- CloseAFPFork := err;
- end; (* CloseAFPFork *)
-
- (* ***** Mount ****** *)
-
- function CreateDiskImage(diskImage : FSSpec; var disk : DiskImageRecordPtr) : OSErr;
- (* See comment for ReadDiskImage. *)
- var
- err : OSErr;
- volumeSessionRec: GetVolSessInfoRec;
- header : DiskCopyHeader;
- junk : OSErr;
- begin
- (* Create the storage for this disk. *)
- disk := DiskImageRecordPtr(NewPtrSysClear(sizeof(DiskImageRecord)));
- err := MemError;
-
- (* Open the data fork of the image using direct AFP calls. *)
- if err = noErr then begin
- err := GetVolSessionInfo(diskImage.vRefNum, volumeSessionRec);
- if err = noErr then begin
- disk^.aspSessionNumber := volumeSessionRec.sessReferenceNumber;
- disk^.afpForkRef := 0;
- err := OpenAFPFork(disk, volumeSessionRec.sessAFPVolID, diskImage.parID, diskImage.name);
- end; (* if *)
- end; (* if *)
-
- (* Now read the image header to calculate diskSize. *)
- if err = noErr then begin
- err := ReadAFPData(disk, 0, sizeof(DiskCopyHeader), @header, nil);
- end; (* if *)
- if err = noErr then begin
- disk^.diskSize := header.dataSize;
- end; (* if *)
-
- (* Clean up. *)
- if err <> noErr then begin
- junk := DestroyDiskImage(disk);
- end; (* if *)
-
- CreateDiskImage := err;
- end; (* CreateDiskImage *)
-
- (* ***** Read ***** *)
-
- function ReadDiskImage(disk : DiskImageRecordPtr;
- offset : longint; count : longint; buffer : Ptr;
- ioCompletion : ProcPtr) : OSErr;
- (* See the implementation part for a description of the external
- interface to this routine.
- This routine exists merely to add 84 to the offset for the read.
- After that it basically calls through to ReadAFPData. I had
- to do this because I want to call ReadAFPData in CreateDiskImage,
- and I didn't want it to add 84 to the offset.
- *)
- begin
- ReadDiskImage := ReadAFPData(disk,
- offset + sizeof(DiskCopyHeader), count, buffer,
- ioCompletion);
- end; (* ReadDiskImage *)
-
- (* ***** Unmount ***** *)
-
- function DestroyDiskImage(disk : DiskImageRecordPtr) : OSErr;
- (* See comment in implementation part. *)
- var
- err : OSErr;
- begin
- err := noErr;
- if disk <> nil then begin
- if disk^.afpForkRef <> 0 then begin
- err := CloseAFPFork(disk);
- end; (* if *)
- DisposePtr(Ptr(disk));
- end; (* if *)
- DestroyDiskImage := err;
- end; (* DestroyDiskImage *)
-
- end. (* DiskImageCore *)